package de.lmu.ifi.dbs.trafficmining.ui.result;

import de.lmu.ifi.dbs.trafficmining.clustering.Cluster;
import de.lmu.ifi.dbs.trafficmining.clustering.Route;
import de.lmu.ifi.dbs.trafficmining.clustering.SingleLinkClusteringWithPreprocessing;
import de.lmu.ifi.dbs.trafficmining.graph.Path;
import de.lmu.ifi.dbs.trafficmining.result.Result;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;
import javax.swing.ToolTipManager;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.TableRowSorter;
import javax.swing.tree.TreePath;

/**
 * @author Franz
 */
public class ResultPanel extends javax.swing.JPanel {

    static final Logger log = Logger.getLogger(ResultPanel.class.getName());
    private Result result;
    private ReadOnlyTableModel resultTableModel = new ReadOnlyTableModel();
    private Map<Integer, Path> tableMap = new HashMap<>();

    public ResultPanel() {
        initComponents();
        initClusterComponents();
    }

    public void setResult(Result result) {
        this.result = result;
        setResultToList();
        startClustering();
    }

    private void startClustering() {
        if (result == null || result.getResults().size() <= 1) {
            clusterTree.setModel(null);
            return;
        }
        SingleLinkClusteringWithPreprocessing skyclus = new SingleLinkClusteringWithPreprocessing();
        skyclus.setInput(result);
        skyclus.start();
        Cluster cluster = skyclus.getResult();
        if (cluster != null) {
            clusterTree.setModel(new ClusterTreeModel(cluster));
            clusterTree.repaint();
        }
    }

    private void initClusterComponents() {
        clusterTree.setModel(new ClusterTreeModel());
        clusterTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent evt) {
                TreePath path = evt.getNewLeadSelectionPath();
                if (path != null) {
                    highlightClusteredRoutes(path.getLastPathComponent());
                }
            }
        });
        ToolTipManager.sharedInstance().registerComponent(clusterTree);
        clusterTree.setCellRenderer(new ClusterTreeCellRenderer());
    }

    private void highlightClusteredRoutes(Object o) {
        List<Route> list = new ArrayList<>();

        if (o instanceof Route) {
            list.add((Route) o);
        } else if (o instanceof Cluster) {
            list = ((Cluster) o).getRoutes();
        }

        resultTable.clearSelection();
        for (Route r : list) {
            resultTable.addRowSelectionInterval(r.getId() - 1, r.getId() - 1);
        }
    }

    private void setResultToList() {
        resultTableModel.setRowCount(0);
        if (result == null) {
            return;
        }

        { // column names
            List<String> cols = new ArrayList<>(result.getUnits());
            cols.add(0, "route");
            resultTableModel.setColumnIdentifiers(cols.toArray());
        }

        {// set columns sortable
            TableRowSorter sorter = new TableRowSorter(resultTableModel);
            resultTable.setRowSorter(sorter);
            for (int i = 0; i < resultTableModel.getColumnCount(); i++) {
                sorter.setComparator(i, new ResultTableColumnSorter());
            }
        }

        Integer i = 1;
        Map<Path, double[]> resultsMap = result.getResults();
        for (Map.Entry<Path, double[]> entry : resultsMap.entrySet()) {
            Object[] row = makeTableDataVector(result, entry.getValue(), i);
            resultTableModel.addRow(row);
            tableMap.put(i, entry.getKey());
            i++;
        }

        if (resultTableModel.getRowCount() > 0) {
            resultTable.getSelectionModel().clearSelection();
            resultTable.getSelectionModel().setSelectionInterval(0, 0);
        }
    }

    private Object[] makeTableDataVector(Result result, double[] cost, int id) {
        List vec = new ArrayList();
        vec.add(id);
        List<String> units = result.getUnits();
        for (int i = 0; i < cost.length; i++) {
            String unit = "";
            if (units.size() > i) {
                unit = units.get(i);
            }
            vec.add(String.format(Locale.US, "%.4f %s", cost[i], unit));
        }
        return vec.toArray();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        javax.swing.JTabbedPane tabPane = new javax.swing.JTabbedPane();
        javax.swing.JScrollPane tableScrollPane = new javax.swing.JScrollPane();
        resultTable = new javax.swing.JTable();
        javax.swing.JScrollPane treeScrollPane = new javax.swing.JScrollPane();
        clusterTree = new javax.swing.JTree();

        setLayout(new java.awt.BorderLayout());

        resultTable.setModel(resultTableModel);
        tableScrollPane.setViewportView(resultTable);

        tabPane.addTab("List", tableScrollPane);

        treeScrollPane.setViewportView(clusterTree);

        tabPane.addTab("Tree", treeScrollPane);

        add(tabPane, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTree clusterTree;
    private javax.swing.JTable resultTable;
    // End of variables declaration//GEN-END:variables

    public void addListSelectionListener(ListSelectionListener listSelectionListener) {
        resultTable.getSelectionModel().addListSelectionListener(listSelectionListener);
    }

    public List<Path> getSelectedPaths() {
        List<Path> l = new ArrayList<>();
        int[] rowIndices = resultTable.getSelectedRows();
        for (int i : rowIndices) {
            Object id = resultTableModel.getValueAt(i, 0);
            l.add(tableMap.get(id));
        }
        return l;
    }
}
